package main;

import java.util.ArrayList;

import javax.swing.table.DefaultTableModel;

/**
 * REGULA FALSI (RF) and SECANT METHOD (SM) 
 */
public class Model {
	private ArrayList<FunctionTerm> function;
	private int itMax; // number of iterations
		
	/* REGULA FALSI */
	private double tolerance; // tolerance
	private double lowerbound; // lower interval
	private double upperbound; // upper interval
	private double rX0, rX1, rX2;
	private double rY0, rY1, rY2;
	private double tempTol;
	
	/* SECANT METHOD */
	private double sX0, sX1, sX2;
	private double sY0, sY1;
	
	public Model(String str, int iteration, double tol, double lb, double ub, double x0, double x1) {
		setFunction(str);
		itMax = iteration;
		tolerance = tol;
		lowerbound = lb;
		upperbound = ub;
		sX0 = x0;
		sX1 = x1;
	}
	
	/**
	 * Rounds off value to 6 decimal places
	 * @param value to be rounded off
	 * @return value rounded off to 6 decimal places
	 */
	public double roundOff(double value) {
		return (double) Math.round(value * 1000000) / 1000000;
	}
	
	/**
	 * Solves the function
	 * @param x value for x in the function
	 * @return the value for f(x)
	 */
	public double solveFunction(double x) {
		double value = 0.0;
		
		for (int i = 0; i < function.size(); i++) {
			value += (function.get(i).getTerm()[0] * Math.pow(x, function.get(i).getTerm()[1]));
			System.out.println("value ("+value+") = "+function.get(i).getTerm()[0]+" "+x+" raised to "+function.get(i).getTerm()[1]);
		}
		
		return roundOff(value);
	}
	
	/**
	 * Algorithm for Regula Falsi. Solves for the next value of X.
	 */
	public void RegulaFalsiAlgorithm() {
		rX2 = roundOff(1.0*(rX0*rY1 - rX1*rY0) / (rY1 - rY0));
		rY2 = solveFunction(rX2);
		tempTol = Math.abs(roundOff(rX1 - rX0));
		
		if (rY2 != 0) {
			if (rY1*rY2 < 0) {
				rX0 = rX2;
				rY0 = rY2;
			} else {
				rX1 = rX2;
				rY1 = rY2;
			}
		}
	}
	
	public DefaultTableModel generateRegulaFalsiTableModel() {
		int itCtr = 1;
		DefaultTableModel rfTblMdl = new DefaultTableModel();

		String [] tableColumnRF = {"i", "X0", "Y0", "X1", "Y1", "X2", "Y2", "tolerance"};
		rfTblMdl.setColumnIdentifiers(tableColumnRF);
		rX0 = lowerbound;
		rX1 = upperbound;

		do {
			Object[] row = new Object[8];
			
			// iterations
			row[0] = itCtr;
			
			// x0
			row[1] = rX0;
			
			// y0
			rY0 = solveFunction(rX0);
			row[2] = rY0;
			
			// x1
			row[3] = rX1;
			
			// y1
			rY1 = solveFunction(rX1);
			row[4] = rY1;
			
			// solve for x2
			// solve for y2
			RegulaFalsiAlgorithm();
			row[5] = rX2;
			row[6] = rY2;
			
			// solve for tol
			row[7] = tempTol;
			
			rfTblMdl.addRow(row);
			
			itCtr++;
		} while (itCtr <= itMax || tempTol <= tolerance || rY2 == 0);

		return rfTblMdl;
	}
	
	public void secantMethodAlgorithm() {
		sY0 = solveFunction(sX0);
		sY1 = solveFunction(sX1);
		sX2 = sX1 - ((sY1 * (sX0 - sX1)) / (sY0 - sY1));
	}
	
	public void setFunction(String given) {
		String[] values = given.split(" ");
		int i = 0;
		int ctr = 0;
		int stopper = values.length/2;
		
		function = new ArrayList<FunctionTerm>();
		
		do {
			FunctionTerm term = new FunctionTerm(Double.parseDouble(values[i]), Double.parseDouble(values[i+1]));
			function.add(term);
			i+=2;
			ctr++;
		} while (ctr < stopper);
	}

	public void setItMax(int itMax) {
		this.itMax = itMax;
	}

	public void setTolerance(double tolerance) {
		this.tolerance = tolerance;
	}

	public void setLowerbound(double lowerbound) {
		this.lowerbound = lowerbound;
	}

	public void setUpperbound(double upperbound) {
		this.upperbound = upperbound;
	}

	public void setsX0(double sX0) {
		this.sX0 = sX0;
	}

	public void setsX1(double sX1) {
		this.sX1 = sX1;
	}
}
